package emailserving.component;

import com.avaje.ebean.EbeanServer;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import emailserving.common.Utils;
import emailserving.config.AppMetadataConfig;
import emailserving.domain.AbstractModel;
import emailserving.domain.Contact;
import emailserving.domain.Content;
import emailserving.domain.Job;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component
public class JobService {
    private static final Logger logger = LoggerFactory.getLogger(JobService.class);

    @Autowired
    EbeanServer ebeanServer;

    @Autowired
    EmailSender mailSender;

    @Autowired
    EmailTaskPool emailTaskPool;

    @Autowired
    AppMetadataConfig appMetadataConfig;

    public void createJob(String jobName, String sender, Content content, List<Contact> contacts) {
        Preconditions.checkNotNull(sender);
        Preconditions.checkNotNull(content);
        Preconditions.checkNotNull(contacts);

        ebeanServer.beginTransaction();
        try {
            Job job = new Job(jobName);
            job.setSenderEmail(sender);
            ebeanServer.save(job);
            contacts.forEach(contact -> {
                contact.setJob(job);
                ebeanServer.save(contact);
            });
            content.setJob(job);
            ebeanServer.save(content);
            ebeanServer.commitTransaction();
            logger.info("create job %d#%s successfully", job.getId(), jobName);
        } finally {
            ebeanServer.endTransaction();
        }
    }

    public void stopJob(long jobId) {
        emailTaskPool.stop(jobId);
    }


    public void executeJob(long jobId) {
        Job job = Job.findById(jobId);
        if (Job.isJobRunnable(job) && !emailTaskPool.existsExecutingTask(jobId)) {
            List<Contact> contacts = Contact.findNotSentContact(jobId);
            Content content = Content.getContentOf(jobId);
            emailTaskPool.registerTasksForJob(
                    jobId,
                    createEmailTasksFromContacts(job.getSenderEmail(), contacts, content));

            emailTaskPool.start(jobId);
            Job.updateJobStartTime(jobId);
        }


    }

    public List<EmailTaskPool.JobExecutingStatus> jobExecutingStatusesOf(List<Job> jobs) {
        Preconditions.checkNotNull(jobs);
        return emailTaskPool.executingStatus(jobs.stream().map(Job::getId).collect(Collectors.toList()));
    }


    private List<EmailTask> createEmailTasksFromContacts(String sender, List<Contact> contacts, Content content) {
        List<EmailTask> emailTasks = Lists.newArrayList();
        for (List<Contact> contactList : Utils.getPaginationList(contacts, 10)) {

            Map<Long, MimeMessage> messages = contactList.stream()
                    .collect(Collectors.toMap(
                            AbstractModel::getId,
                            c -> createMimeMessage(
                                    sender,
                                    c.getEmail(),
                                    content.getText(),
                                    content.getSubject())));

            EmailTask emailTask = new EmailTask(messages, mailSender);
            emailTask.setCallback(Contact::updateContactSentTime);
            emailTasks.add(emailTask);
        }
        logger.debug("email tasks size: " + emailTasks.size());
        return emailTasks;
    }

    public MimeMessage createMimeMessage(String from, String to, String content, String subject) {
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, false, "utf-8");
            helper.setText(content, true);
            helper.setSubject(subject);
            helper.setTo(to);
            helper.setFrom(from);
        } catch (MessagingException e) {
            logger.error("create mimeMessage failed", e);
        }
        return mimeMessage;
    }

    public List<Job> findJobNameLike(String name, int pageIndex) {
        return Job.findNameLike(name, pageIndex, appMetadataConfig.getDefaultPageSize());
    }


    public void refresh() {
        Job.refreshAllJobsExecutingStatus();
        logger.info("refresh jobs status");
    }


}
